package org.mtmi.ui.controls;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.mtmi.io.IControlIO;
import org.mtmi.io.controls.KnobIO;
import org.mtmi.ui.GraphicUtil;
import org.mtmi.ui.controls.edits.KnobControlEditor;
import org.mtmi.ui.edit.IControlEditor;
import org.mtmi.ui.layouts.LayoutData;
import org.mtmi.ui.links.IConnectionOutlinePointProvider;
import org.mtmi.ui.links.OvalConnectionProvider;
import org.mtmi.ui.scenes.CLR;
import org.mtmi.ui.scenes.COLOR;
import org.mtmi.ui.scenes.Scene;

public class Knob extends AbstractKnob
{
	public static final int PROGRESSIVE = 0x00000; // must use different value than M.xxx flags
	public static final int SYMETRIC    = 0x10000;
	
	private Color clr_value, clr_dot;

	public Knob( Scene scene, int flags ) {
		this( scene, 0, 0, 0, flags );
	}
	
	/** 
	 * Create a vectorial Knob.<p>
	 * Foreground is the center of knob color.
	 * @param scene  Scene to place knob
	 * @param _flags common flag, plus {@link #PROGRESSIVE} or {@link #SYMETRIC} (progressive by default)
	 */
	public Knob(Scene scene, int valCurrent, int valMin, int valMax, int _flags ) {
		super(scene, valCurrent, valMin, valMax, _flags);
		setSize( computeSize( null ) );
		clr_value = COLOR.red; //value arc color
		clr_dot  = COLOR.grey20; //dot over value color
		setForeground( COLOR.grey70 ); //knob center color
		setBackground( COLOR.grey50 ); 
	}
	
	@Override
	public Point computeSize( LayoutData l ) {
		return new Point( 75, 75 );
	}
	
	public void setValueColor( Color c ) {
		clr_value = c;
		redraw();
	}
	
	public void setDotColor( Color c ) {
		clr_dot= c;
		redraw();
	}
	
	public Color getValueColor() { return clr_value; }
	public Color getDotColor() { return clr_dot; }
	
	/** 
	 * @return true if Knob is progressive (arc value is displayed from minimum to max),
	 * false for symetric (arc value is displayed from middle to minimum or max ). 
	 */
	public boolean isProgressive() { return (flags & SYMETRIC)==PROGRESSIVE; }

	
	public void setProgressive( boolean p ) {
		flags = (flags & ~SYMETRIC ) | ( p ? PROGRESSIVE : SYMETRIC );
	}
	
	/** draw knob using gc's background for knob and foreground for border */
	public static int drawKnob( GC gc, int cx, int cy, int w, int h, int vmin, int value, int vmax, int angle_vmin, int angle_move )
	{
		int kx = w/2, ky=h/2;
		gc.fillOval( cx-kx/2, cy-ky/2, kx, ky );
		gc.drawOval( cx-kx/2, cy-ky/2, kx, ky );
		
		Color clr_knob = gc.getBackground();
		Color cl = CLR.lighter( clr_knob, 0.5f );
		Color cd = CLR.darker( clr_knob, 0.7f );
		gc.setForeground( cl );
		gc.drawArc(  cx-kx/2+1, cy-ky/2+1, kx-2, ky-2, 45, 180 );
		gc.setForeground( cd );
		gc.drawArc(  cx-kx/2+1, cy-ky/2+1, kx-2, ky-2, 45, -180 );
		
		int ia = Math.round( angle_move*(value-vmin)/(vmax-vmin) );
		double a = Math.PI*(angle_vmin+ia)/180;
		
		int x1 = cx+(int)Math.round( kx*Math.cos( a ) /8 );
		int y1 = cy-(int)Math.round( ky*Math.sin( a ) /8 );
		int x2 = cx+(int)Math.round( 3*kx*Math.cos( a ) /8 );
		int y2 = cy-(int)Math.round( 3*ky*Math.sin( a ) /8 );
		
		gc.setForeground( cd );
		gc.setLineCap( SWT.CAP_ROUND );
		gc.setLineWidth(borderWidth);
		gc.drawLine( x1, y1, x2, y2 );
		gc.setLineCap( SWT.CAP_FLAT );
		
		cl.dispose();
		cd.dispose();
		
		return ia;
	}
	
	private static final int OW=2; //outline width (oval line may expand outside bounds)
	
	@Override
	public void paint(GC gc)
	{
		gc.setAntialias(SWT.ON);
		gc.setTextAntialias(SWT.ON);
		gc.setLineWidth( OW );
		
		Color bg = getBackground();
		gc.setBackground( bg );
		gc.fillOval( location.x, location.y, size.x, size.y );
		Color border = CLR.lighter( bg, 0.3f );
		gc.setForeground( border );
		gc.drawOval( location.x, location.y, size.x, size.y );
		border.dispose();
		
		Color clr_knob = getForeground();
		//knob center:
		Color c=null;
		if( !isMoving() ) {
			gc.setBackground( clr_knob );
		} else {
			c = CLR.lighter( clr_knob, 0.5f );
			gc.setBackground( c );
		}
		Color bk1 = CLR.darker( clr_knob, 0.5f );
		gc.setForeground( bk1 );
		
		int cx = getCX();
		int cy = getCY();
		int amin = getAngleVMin();
		int amove = getDAngle();
		int da = drawKnob( gc, cx, cy,size.x,size.y, getMinValue(),getValue(),getMaxValue(), amin, amove );
		bk1.dispose();
		
		//value:
		int lw = Math.min( size.x, size.y ) /8;
		gc.setLineWidth( lw );
		gc.setForeground( clr_value );
		if( isProgressive() ) {
			gc.drawArc( location.x+size.x/8, location.y+size.y/8, Math.round(3*size.x/4f), Math.round(3*size.y/4f), amin, da );
		} else {
			int amove2 = amove/2;
			int amiddle = amin+amove2;
			gc.drawArc( location.x+size.x/8, location.y+size.y/8, Math.round(3*size.x/4f), Math.round(3*size.y/4f), amiddle, da-amove2 );
		}
		
		//dots over value:
		gc.setLineWidth( 0 );
		gc.setForeground( clr_dot );
		final int div=10; //TODO might be a parameter !!
		double dda = Math.PI*(amove/div)/180;
		double ag0 = Math.PI*amin/180;
		double aa=ag0;
		for( int i=0; i<=div; i++ ) {				
			drawDot( cx, cy, gc, aa );
			aa += dda;
		}

		if( c!=null ) c.dispose();
	}
	
	private void drawDot( int cx, int cy, GC gc, double angle_radian )
	{
		double cos = Math.cos( angle_radian );
		double sin = Math.sin( angle_radian );
		int w2 = size.x/2, h2 = size.y/2;
		int x1 = (int)Math.round( (w2-3)*cos );
		int y1 = (int)Math.round( (h2-3)*sin );
		int x2 = (int)Math.round( (w2-size.x/4+3)*cos );
		int y2 = (int)Math.round( (h2-size.y/4+3)*sin );
		gc.drawLine( cx+x1, cy-y1, cx+x2, cy-y2 );
	}

	@Override
	/** @return true if point if contained in knob (background) circle) */
	public boolean containsTouch(TouchControlEvent e) {
		int cx = getCX();
		int cy = getCY();
		int r = Math.min( size.x/2, size.y/2 );
		//note: this is wrong if knob is not square
		return GraphicUtil.circleContains( cx, cy, r, e.x, e.y );
	}

	@Override
	public Object getAdapter( Class<?> clazz, Object arg ) {
		if( clazz==IControlEditor.class ) {
			return new KnobControlEditor( this );
		}
		else if( clazz == IConnectionOutlinePointProvider.class ) {
			return new OvalConnectionProvider( this );
		}
		else if( clazz == IControlIO.class ) {
			return new KnobIO();
		}
		return super.getAdapter( clazz, arg );
	}
	
	@Override
	/** @return OvalConnectionProvider */
	public IConnectionOutlinePointProvider getConnectionAnchor( String connection_id ) {
		return new OvalConnectionProvider( this );
	}
	
	@Override
	public Rectangle getRedrawArea(Rectangle r) {
		r =  super.getRedrawArea( r );
		//include outline line width:
		GraphicUtil.expandRect( r, OW );
		return r;
	}
}
